<script setup>
import { ref, onMounted, onBeforeUnmount, watch, nextTick } from 'vue';
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { ElMessage, ElTooltip, ElButton } from 'element-plus';

const props = defineProps({
  selectedMeridian: Object,
  selectedAcupoint: Object,
});

// References
const container = ref(null);
const loadingProgress = ref(0);
const isLoading = ref(true);
const loadingError = ref(false);
const errorMessage = ref('');
const viewMode = ref('normal'); // normal, wireframe, x-ray

// Three.js variables
let scene, camera, renderer, controls, model, mixer;
let clock = new THREE.Clock();
let meridianHighlights = {};
let acupointMarkers = {};
let animationFrameId = null;
let meridianPaths = {};
let acupointPositions = {};

// Materials for different purposes
const highlightMeridianMaterial = new THREE.MeshStandardMaterial({
  color: 0xff3333,
  emissive: 0xff0000,
  emissiveIntensity: 0.7,
  transparent: true,
  opacity: 0.8,
  roughness: 0.3,
  metalness: 0.8
});

const highlightAcupointMaterial = new THREE.MeshBasicMaterial({
  color: 0xffaa00,
  emissive: 0xffaa00,
  emissiveIntensity: 1.0,
  transparent: true,
  opacity: 0.9
});

const xRayMaterial = new THREE.MeshBasicMaterial({
  color: 0x00aaff,
  transparent: true,
  opacity: 0.3,
  wireframe: false,
  depthWrite: false,
  side: THREE.DoubleSide
});

const wireframeMaterial = new THREE.MeshBasicMaterial({
  color: 0x00ff00,
  wireframe: true
});

// Function to center model and camera
const centerModel = () => {
  if (!model || !camera || !controls) return;
  
  // Get model bounding box
  const boundingBox = new THREE.Box3().setFromObject(model);
  const center = new THREE.Vector3();
  boundingBox.getCenter(center);
  
  // Set camera position
  camera.position.set(0, center.y, center.z + 3);
  camera.lookAt(center);
  
  // Update controls target
  controls.target.copy(center);
  controls.update();
};

// Reset camera to default position (centered)
const resetCamera = () => {
  if (!camera || !controls || !model) return;
  centerModel();
};

// Setup the scene
const setupScene = () => {
  // Create scene
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0xf0f0f0);

  // Create camera
  camera = new THREE.PerspectiveCamera(
    45,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000
  );
  camera.position.set(0, 1.5, 3);

  // Create renderer
  renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  container.value.appendChild(renderer.domElement);

  // Add lights
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
  directionalLight.position.set(5, 10, 5);
  directionalLight.castShadow = true;
  
  // Improve shadow quality
  directionalLight.shadow.mapSize.width = 1024;
  directionalLight.shadow.mapSize.height = 1024;
  directionalLight.shadow.camera.near = 0.5;
  directionalLight.shadow.camera.far = 50;
  scene.add(directionalLight);
  
  // Add fill light from opposite side
  const fillLight = new THREE.DirectionalLight(0xffffff, 0.3);
  fillLight.position.set(-5, 5, -5);
  scene.add(fillLight);

  // Add a small light from below for better visibility
  const rimLight = new THREE.DirectionalLight(0xffffff, 0.2);
  rimLight.position.set(0, -5, 0);
  scene.add(rimLight);

  // Create controls
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.1;
  controls.minDistance = 1.0;
  controls.maxDistance = 6;
  controls.maxPolarAngle = Math.PI - 0.1; // Prevent going below the model
  controls.minPolarAngle = 0.1; // Prevent going too high above the model
  controls.enablePan = true;
  controls.screenSpacePanning = true;
  controls.rotateSpeed = 0.7;
  controls.zoomSpeed = 1.2;
  controls.panSpeed = 0.8;
  controls.target.set(0, 1, 0);
};

// Load the 3D model
const loadModel = () => {
  const loader = new GLTFLoader();
  
  loader.load(
    '/models/human_meridian_system-processed.glb',
    (gltf) => {
      model = gltf.scene;
      
      // Adjust scale and position if needed
      model.scale.set(1, 1, 1);
      model.position.set(0, 0, 0); // Center the model
      
      // Make sure model casts and receives shadows
      model.traverse((node) => {
        if (node.isMesh) {
          node.castShadow = true;
          node.receiveShadow = true;
          
          // Store original material for later restoration
          node.userData.originalMaterial = node.material.clone();
          
          // Enable material properties for lighting
          node.material.roughness = 0.6;
          node.material.metalness = 0.1;
        }
      });
      
      scene.add(model);

      // Set initial rotation to show front view
      model.rotation.y = Math.PI; // Rotate to face camera
      
      // Setup animations if they exist
      if (gltf.animations && gltf.animations.length) {
        mixer = new THREE.AnimationMixer(model);
        const action = mixer.clipAction(gltf.animations[0]);
        action.play();
      }
      
      // Create a mapping of meridians and acupoints
      setupMeridianMaps();
      
      // Center the model and camera - crucial for initial view
      centerModel();
      
      // Try to highlight anything that's already selected
      nextTick(() => {
        if (props.selectedMeridian) {
          highlightMeridian(props.selectedMeridian.no);
        }
        if (props.selectedAcupoint) {
          highlightAcupoint(props.selectedAcupoint.no);
        }
      });
      
      isLoading.value = false;
    },
    (xhr) => {
      loadingProgress.value = Math.floor((xhr.loaded / xhr.total) * 100);
    },
    (error) => {
      console.error('Error loading model:', error);
      isLoading.value = false;
      loadingError.value = true;
      errorMessage.value = '无法加载3D模型，请刷新重试: ' + error.message;
      ElMessage.error('3D模型加载失败，请刷新重试');
    }
  );
};

// Setup meridian maps for highlighting
const setupMeridianMaps = () => {
  if (!model) return;
  
  // Define known meridian codes and their common names in the model
  const meridianMappings = {
    'LU': ['lung', 'fei', 'hand-taiyin'],
    'LI': ['large-intestine', 'dachang', 'hand-yangming'],
    'ST': ['stomach', 'wei', 'foot-yangming'],
    'SP': ['spleen', 'pi', 'foot-taiyin'],
    'HT': ['heart', 'xin', 'hand-shaoyin'],
    'SI': ['small-intestine', 'xiaochang', 'hand-taiyang'],
    'BL': ['bladder', 'pangguang', 'foot-taiyang'],
    'KI': ['kidney', 'shen', 'foot-shaoyin'],
    'PC': ['pericardium', 'xinbao', 'hand-jueyin'],
    'TE': ['triple-energizer', 'sanjiao', 'hand-shaoyang'],
    'GB': ['gallbladder', 'dan', 'foot-shaoyang'],
    'LR': ['liver', 'gan', 'foot-jueyin'],
    'DU': ['governing', 'dumai', 'du-mai'],
    'REN': ['conception', 'renmai', 'ren-mai']
  };
  
  // Initialize meridian paths
  for (const code in meridianMappings) {
    meridianPaths[code] = [];
  }
  
  // Initialize acupoint positions map
  acupointPositions = {};
  
  // Scan the model for meridian paths and acupoints
  model.traverse((child) => {
    if (child.isMesh) {
      // Store original material
      child.userData.originalMaterial = child.material.clone();
      
      const name = child.name.toLowerCase();
      
      // Check if this mesh is part of a meridian path
      for (const [code, keywords] of Object.entries(meridianMappings)) {
        if (keywords.some(keyword => name.includes(keyword)) || name.includes(code.toLowerCase())) {
          meridianPaths[code].push(child);
          child.userData.meridianCode = code;
          break;
        }
      }
      
      // Check if this mesh is an acupoint
      // Acupoints usually follow a pattern like GB20, ST36, etc.
      const acupointMatch = child.name.match(/([A-Z]{2})(\d+)/i);
      if (acupointMatch) {
        const code = acupointMatch[1].toUpperCase();
        const pointNumber = acupointMatch[2];
        const acupointCode = `${code}${pointNumber}`;
        
        // Store the acupoint position (center of the mesh)
        const box = new THREE.Box3().setFromObject(child);
        const center = new THREE.Vector3();
        box.getCenter(center);
        
        acupointPositions[acupointCode] = {
          mesh: child,
          position: center,
          code: acupointCode
        };
        
        child.userData.acupointCode = acupointCode;
      }
    }
  });
  
  console.log(`Mapped ${Object.keys(meridianPaths).length} meridian paths`);
  console.log(`Mapped ${Object.keys(acupointPositions).length} acupoint positions`);
};

// Change view mode (normal, wireframe, x-ray)
const setViewMode = (mode) => {
  if (!model) return;
  
  viewMode.value = mode;
  
  // Reset all materials first
  model.traverse((child) => {
    if (child.isMesh && child.userData.originalMaterial) {
      child.material = child.userData.originalMaterial.clone();
    }
  });
  
  // Clear any highlights
  clearHighlights();
  clearAcupointMarkers();
  
  // Apply the selected view mode
  model.traverse((child) => {
    if (child.isMesh) {
      switch (mode) {
        case 'wireframe':
          child.material = wireframeMaterial.clone();
          break;
        case 'x-ray':
          child.material = xRayMaterial.clone();
          break;
        case 'normal':
        default:
          // Already reset to original above
          break;
      }
    }
  });
  
  // Re-apply highlights if needed
  if (props.selectedMeridian) {
    highlightMeridian(props.selectedMeridian.no);
  }
  if (props.selectedAcupoint) {
    highlightAcupoint(props.selectedAcupoint.no);
  }
};

// Highlight a meridian
const highlightMeridian = (meridianNo) => {
  // Clear any existing highlights
  clearHighlights();
  
  // If no model or no meridian number, exit
  if (!model || !meridianNo) return;
  
  // Extract meridian code (e.g., "ST" from "ST-1")
  const meridianCode = meridianNo.replace(/[-\d]/g, '');
  
  // First check if we have mapped this meridian
  if (meridianPaths[meridianCode] && meridianPaths[meridianCode].length > 0) {
    // Highlight all meshes associated with this meridian
    for (const mesh of meridianPaths[meridianCode]) {
      // Store original material if not already stored
      if (!mesh.userData.originalMaterial) {
        mesh.userData.originalMaterial = mesh.material.clone();
      }
      
      // Apply highlight material
      mesh.material = highlightMeridianMaterial.clone();
      meridianHighlights[mesh.uuid] = mesh;
    }
    
    console.log(`Highlighted ${meridianPaths[meridianCode].length} meshes for meridian ${meridianCode}`);
  } else {
    // If no specific meshes found, create a visual path using acupoints
    console.log(`Could not find specific meshes for meridian ${meridianCode}. Creating visual path.`);
    createMeridianPath(meridianCode);
  }
};

// Create a visual meridian path when specific meshes not found
const createMeridianPath = (meridianCode) => {
  // Find all acupoints that belong to this meridian
  const relatedPoints = [];
  for (const [code, pointData] of Object.entries(acupointPositions)) {
    if (code.startsWith(meridianCode)) {
      relatedPoints.push(pointData);
    }
  }
  
  if (relatedPoints.length === 0) {
    console.log(`No acupoints found for meridian ${meridianCode}`);
    return;
  }
  
  // Sort points by their number
  relatedPoints.sort((a, b) => {
    const numA = parseInt(a.code.replace(/[A-Z]/g, ''));
    const numB = parseInt(b.code.replace(/[A-Z]/g, ''));
    return numA - numB;
  });
  
  // Create a path geometry
  const points = relatedPoints.map(point => point.position);
  const curve = new THREE.CatmullRomCurve3(points);
  const geometry = new THREE.TubeGeometry(curve, points.length * 10, 0.02, 8, false);
  const material = new THREE.MeshStandardMaterial({ 
    color: 0xff3333,
    emissive: 0xff0000,
    emissiveIntensity: 0.7,
    transparent: true,
    opacity: 0.8
  });
  const tube = new THREE.Mesh(geometry, material);
  scene.add(tube);
  
  // Add to highlight objects for later removal
  meridianHighlights['path'] = tube;
  
  // Also highlight the acupoints along this meridian
  for (const pointData of relatedPoints) {
    const mesh = pointData.mesh;
    if (mesh) {
      if (!mesh.userData.originalMaterial) {
        mesh.userData.originalMaterial = mesh.material.clone();
      }
      
      mesh.material = highlightMeridianMaterial.clone();
      meridianHighlights[mesh.uuid] = mesh;
    }
  }
  
  console.log(`Created meridian path with ${relatedPoints.length} points`);
};

// Highlight an acupoint
const highlightAcupoint = (acupointNo) => {
  // Clear any existing acupoint markers
  clearAcupointMarkers();
  
  if (!model || !acupointNo) return;
  
  let acupointFound = false;
  
  // First check if we have the acupoint mapped
  if (acupointPositions[acupointNo]) {
    const pointData = acupointPositions[acupointNo];
    
    // If it has a mesh, highlight it
    if (pointData.mesh) {
      const mesh = pointData.mesh;
      
      // Store original material
      if (!mesh.userData.originalMaterial) {
        mesh.userData.originalMaterial = mesh.material.clone();
      }
      
      // Apply highlight material
      mesh.material = highlightAcupointMaterial.clone();
      acupointMarkers[mesh.uuid] = mesh;
      mesh.userData.isAcupointMarker = true;
      
      // Move camera to focus on this acupoint
      controls.target.copy(pointData.position);
      controls.update();
      
      acupointFound = true;
    }
  }
  
  // If acupoint not found in the map, try to find by name
  if (!acupointFound) {
    model.traverse((child) => {
      if (child.isMesh) {
        // Check if mesh name contains the acupoint code
        if (child.name.includes(acupointNo) || 
            (child.userData.acupointCode && child.userData.acupointCode === acupointNo)) {
          
          // Store original material
          if (!child.userData.originalMaterial) {
            child.userData.originalMaterial = child.material.clone();
          }
          
          // Apply highlight material
          child.material = highlightAcupointMaterial.clone();
          acupointMarkers[child.uuid] = child;
          child.userData.isAcupointMarker = true;
          
          acupointFound = true;
        }
      }
    });
  }
  
  // If still not found, create a marker at an approximated position
  if (!acupointFound) {
    // Parse the acupoint code to get meridian and point number
    const matches = acupointNo.match(/([A-Z]{2})(\d+)/);
    if (!matches) {
      console.log(`Invalid acupoint code format: ${acupointNo}`);
      return;
    }
    
    const meridianCode = matches[1];
    const pointNumber = parseInt(matches[2]);
    
    // Find related acupoints on the same meridian
    const relatedPoints = [];
    for (const [code, pointData] of Object.entries(acupointPositions)) {
      if (code.startsWith(meridianCode)) {
        relatedPoints.push({
          ...pointData,
          number: parseInt(code.replace(/[A-Z]/g, ''))
        });
      }
    }
    
    if (relatedPoints.length === 0) {
      console.log(`No reference points found for meridian ${meridianCode}`);
      createDefaultAcupointMarker(acupointNo);
      return;
    }
    
    // Sort points by their number
    relatedPoints.sort((a, b) => a.number - b.number);
    
    // Find points that bracket our target point number
    let position;
    
    // Find points that bracket our target point number
    const lowerPoint = relatedPoints.filter(p => p.number <= pointNumber).pop();
    const higherPoint = relatedPoints.filter(p => p.number >= pointNumber).shift();
    
    if (lowerPoint && higherPoint) {
      // Interpolate between the two nearest points
      const ratio = (pointNumber - lowerPoint.number) / (higherPoint.number - lowerPoint.number);
      position = new THREE.Vector3().lerpVectors(lowerPoint.position, higherPoint.position, ratio);
    } else if (lowerPoint) {
      // If we only have lower points, use the highest
      position = lowerPoint.position.clone();
    } else if (higherPoint) {
      // If we only have higher points, use the lowest
      position = higherPoint.position.clone();
    } else {
      // Fallback to default
      createDefaultAcupointMarker(acupointNo);
      return;
    }
    
    // Create a marker at the interpolated position
    createAcupointMarker(position, acupointNo);
    
    // Move camera to focus on this acupoint
    controls.target.copy(position);
    controls.update();
  }
  
  // Show message about the highlighted acupoint
  ElMessage({
    message: `正在显示穴位: ${acupointNo}`,
    type: 'success',
    duration: 2000
  });
};

// Create a default acupoint marker when no reference points are available
const createDefaultAcupointMarker = (acupointNo) => {
  // Simple mapping of meridians to body regions for fallback positioning
  const meridianCode = acupointNo.replace(/\d+/g, '');
  const acupointNumber = parseInt(acupointNo.replace(/\D+/g, '')) || 1;
  
  // Simple mapping of meridians to body regions for demo purposes
  const positionMap = {
    'BL': { x: 0.15, y: acupointNumber / 20, z: -0.1 }, // Back
    'GB': { x: 0.25, y: acupointNumber / 30, z: 0 },    // Side
    'ST': { x: 0.15, y: acupointNumber / 30, z: 0.1 },  // Front
    'LI': { x: 0.3, y: 1.5 - acupointNumber / 30, z: 0.1 }, // Arm
    'LU': { x: 0.2, y: 1.4 - acupointNumber / 30, z: 0.1 }, // Chest/arm
    'SP': { x: 0.1, y: 0.7 - acupointNumber / 40, z: 0.1 },  // Leg
    'LR': { x: 0.08, y: 0.6 - acupointNumber / 40, z: 0.05 }, // Leg
    'KI': { x: 0.05, y: 0.6 - acupointNumber / 40, z: 0 },    // Back/leg
    'HT': { x: 0.15, y: 1.3 - acupointNumber / 30, z: 0.1 },  // Arm
    'PC': { x: 0.12, y: 1.3 - acupointNumber / 30, z: 0.08 }, // Arm
    'SI': { x: 0.25, y: 1.3 - acupointNumber / 30, z: -0.1 }, // Back/arm
    'TE': { x: 0.28, y: 1.3 - acupointNumber / 30, z: 0.05 }  // Arm
  };
  
  const position = positionMap[meridianCode] || { x: 0, y: 1, z: 0 };
  const pos = new THREE.Vector3(position.x, position.y, position.z);
  
  createAcupointMarker(pos, acupointNo);
  
  // For bilateral meridians, create a marker on both sides
  if (['BL', 'GB', 'ST', 'LI', 'SP', 'LR', 'KI', 'TE', 'SI', 'LU'].includes(meridianCode)) {
    const rightPos = new THREE.Vector3(-position.x, position.y, position.z);
    createAcupointMarker(rightPos, acupointNo, 'right-');
  }
  
  // Move camera to focus on this acupoint
  controls.target.copy(pos);
  controls.update();
};

// Create an acupoint marker at the specified position
const createAcupointMarker = (position, acupointNo, prefix = '') => {
  // Create a sphere geometry for the marker
  const markerGeometry = new THREE.SphereGeometry(0.025, 32, 32);
  const markerMaterial = highlightAcupointMaterial.clone();
  
  const marker = new THREE.Mesh(markerGeometry, markerMaterial);
  marker.position.copy(position);
  scene.add(marker);
  
  // Add a pulsing light to make it more visible
  const pointLight = new THREE.PointLight(0xffaa00, 1, 0.2);
  pointLight.position.copy(position);
  scene.add(pointLight);
  
  acupointMarkers[prefix + acupointNo] = marker;
  acupointMarkers[prefix + acupointNo + '-light'] = pointLight;
  
  // Add pulsing animation properties
  marker.userData.isAcupointMarker = true;
  marker.userData.pulseLight = pointLight;
  marker.userData.baseScale = 1.0;
  marker.userData.baseIntensity = 1.0;
};

// Clear all highlights
const clearHighlights = () => {
  // Remove any path elements first
  if (meridianHighlights['path']) {
    scene.remove(meridianHighlights['path']);
    delete meridianHighlights['path'];
  }
  
  // Restore original materials for meshes
  Object.values(meridianHighlights).forEach(mesh => {
    if (mesh.userData && mesh.userData.originalMaterial) {
      mesh.material = mesh.userData.originalMaterial.clone();
    }
  });
  
  meridianHighlights = {};
};

// Clear acupoint markers
const clearAcupointMarkers = () => {
  Object.entries(acupointMarkers).forEach(([key, marker]) => {
    // Remove lights
    if (key.endsWith('-light')) {
      scene.remove(marker);
    }
    // If it's a separate marker object (not part of the model)
    else if (!marker.userData || !marker.userData.originalMaterial) {
      scene.remove(marker);
    } else {
      // If it's part of the model, restore original material
      marker.material = marker.userData.originalMaterial.clone();
    }
  });
  
  acupointMarkers = {};
};

// Animation loop
const animate = () => {
  animationFrameId = requestAnimationFrame(animate);
  
  if (controls) controls.update();
  
  if (mixer) {
    mixer.update(clock.getDelta());
  }
  
  // Update pulsing animations for acupoint markers
  Object.values(acupointMarkers).forEach(marker => {
    if (marker.userData && marker.userData.isAcupointMarker) {
      // Calculate pulse factor using sine wave
      const pulseFactor = 0.5 + 0.5 * Math.sin(Date.now() * 0.005);
      
      // Scale the marker for a pulsing effect
      const scale = marker.userData.baseScale * (1 + 0.3 * pulseFactor);
      marker.scale.set(scale, scale, scale);
      
      // Adjust opacity for better visibility
      if (marker.material) {
        marker.material.opacity = 0.4 + 0.6 * pulseFactor;
      }
      
      // Adjust light intensity if there's an associated light
      if (marker.userData.pulseLight) {
        marker.userData.pulseLight.intensity = marker.userData.baseIntensity * (0.5 + 1.5 * pulseFactor);
      }
    }
  });
  
  renderer.render(scene, camera);
};

// Handle window resize
const handleResize = () => {
  if (!camera || !renderer || !container.value) return;
  
  camera.aspect = container.value.clientWidth / container.value.clientHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
};

// Watch for changes in selected meridian and acupoint
watch(() => props.selectedMeridian, (newMeridian) => {
  if (newMeridian) {
    highlightMeridian(newMeridian.no);
  } else {
    clearHighlights();
  }
}, { deep: true });

watch(() => props.selectedAcupoint, (newAcupoint) => {
  if (newAcupoint) {
    highlightAcupoint(newAcupoint.no);
  } else {
    clearAcupointMarkers();
  }
}, { deep: true });

// Function to take a screenshot
const takeScreenshot = () => {
  if (!renderer) return;
  
  // Render a frame to ensure latest state
  renderer.render(scene, camera);
  
  // Get the data URL from the canvas
  try {
    const dataURL = renderer.domElement.toDataURL('image/png');
    
    // Create a temporary link element
    const link = document.createElement('a');
    link.href = dataURL;
    link.download = `meridian-view-${new Date().toISOString().slice(0,10)}.png`;
    
    // Trigger download
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
    
    ElMessage.success('截图已保存');
  } catch (e) {
    console.error('Screenshot failed:', e);
    ElMessage.error('截图失败');
  }
};

// Lifecycle hooks
onMounted(() => {
  if (container.value) {
    setupScene();
    loadModel();
    window.addEventListener('resize', handleResize);
    animate();
  }
});

onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize);
  
  if (animationFrameId) {
    cancelAnimationFrame(animationFrameId);
  }
  
  if (renderer) {
    renderer.dispose();
    if (container.value) {
      container.value.removeChild(renderer.domElement);
    }
  }
  
  // Clean up THREE.js resources
  if (scene) {
    scene.traverse((object) => {
      if (object.geometry) {
        object.geometry.dispose();
      }
      
      if (object.material) {
        if (Array.isArray(object.material)) {
          for (let i = 0; i < object.material.length; i++) {
            object.material[i].dispose();
          }
        } else {
          object.material.dispose();
        }
      }
    });
  }
});
</script>

<template>
  <div class="model-viewer">
    <!-- Loading overlay -->
    <div v-if="isLoading" class="loading-overlay">
      <div class="loading-content">
        <div class="progress-bar">
          <div class="progress" :style="{ width: `${loadingProgress}%` }"></div>
        </div>
        <div class="loading-text">加载3D模型... {{ loadingProgress }}%</div>
      </div>
    </div>
    
    <!-- Error overlay -->
    <div v-if="loadingError" class="error-overlay">
      <div class="error-content">
        <i class="el-icon-error"></i>
        <div class="error-message">{{ errorMessage }}</div>
        <ElButton type="primary" @click="loadModel">重新加载</ElButton>
      </div>
    </div>
    
    <!-- 3D Model container -->
    <div ref="container" class="model-container"></div>
    
    <!-- Controls bar -->
    <div class="model-controls">
      <div class="control-group">
        <ElTooltip content="重置视角" placement="top">
          <ElButton circle icon="Refresh" @click="resetCamera"></ElButton>
        </ElTooltip>
        
        <ElTooltip content="截图" placement="top">
          <ElButton circle icon="Camera" @click="takeScreenshot"></ElButton>
        </ElTooltip>
      </div>
      
      <div class="view-mode-controls">
        <ElTooltip content="正常视图" placement="top">
          <ElButton
            :type="viewMode === 'normal' ? 'primary' : 'default'"
            @click="setViewMode('normal')"
          >
            正常
          </ElButton>
        </ElTooltip>
        
        <ElTooltip content="线框视图" placement="top">
          <ElButton
            :type="viewMode === 'wireframe' ? 'primary' : 'default'"
            @click="setViewMode('wireframe')"
          >
            线框
          </ElButton>
        </ElTooltip>
        
        <ElTooltip content="透视视图" placement="top">
          <ElButton
            :type="viewMode === 'x-ray' ? 'primary' : 'default'"
            @click="setViewMode('x-ray')"
          >
            透视
          </ElButton>
        </ElTooltip>
      </div>
    </div>
    
    <!-- Controls hint -->
    <div class="controls-hint">
      <i class="el-icon-mouse"></i> 旋转视角 |
      <i class="el-icon-d-arrow-right"></i> 缩放 |
      <i class="el-icon-rank"></i> 平移
    </div>
  </div>
</template>

<style scoped>
.model-viewer {
  position: relative;
  width: 100%;
  height: 100%;
  background-color: #f8f8f8;
  overflow: hidden;
  border-radius: 8px;
  box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.05);
}

.model-container {
  width: 100%;
  height: 100%;
}

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
}

.loading-content {
  width: 80%;
  max-width: 400px;
  text-align: center;
  padding: 30px;
  background-color: rgba(255, 255, 255, 0.1);
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}

.progress-bar {
  width: 100%;
  height: 8px;
  background-color: rgba(255, 255, 255, 0.2);
  border-radius: 4px;
  overflow: hidden;
  margin-bottom: 15px;
}

.progress {
  height: 100%;
  background-color: var(--primary-color);
  border-radius: 4px;
  transition: width 0.3s ease;
}

.loading-text {
  color: white;
  font-size: 16px;
  font-weight: 500;
}

.error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);
  backdrop-filter: blur(4px);
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 10;
}

.error-content {
  width: 80%;
  max-width: 400px;
  text-align: center;
  padding: 30px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 12px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
}

.error-content i {
  font-size: 40px;
  color: #f56c6c;
  margin-bottom: 20px;
}

.error-message {
  color: #606266;
  margin-bottom: 20px;
}

.model-controls {
  position: absolute;
  bottom: 40px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  z-index: 5;
}

.control-group {
  display: flex;
  gap: 10px;
}

.view-mode-controls {
  display: flex;
  gap: 5px;
  background-color: rgba(255, 255, 255, 0.8);
  padding: 5px;
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

.controls-hint {
  position: absolute;
  bottom: 10px;
  left: 0;
  width: 100%;
  text-align: center;
  color: #606266;
  font-size: 12px;
  padding: 5px;
  background-color: rgba(255, 255, 255, 0.8);
  border-radius: 4px;
  backdrop-filter: blur(2px);
}

/* Icon styles */
.el-icon-mouse:before {
  content: "🖱️";
}

.el-icon-d-arrow-right:before {
  content: "🖲️";
}

.el-icon-rank:before {
  content: "👆";
}

.el-icon-error:before {
  content: "❌";
}

@media (max-width: 768px) {
  .model-controls {
    bottom: 50px;
  }
  
  .view-mode-controls {
    flex-direction: row;
  }
}
</style>